#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import time
import os
import sys
from collections import namedtuple
from stat import * # ST_SIZE etc
import subprocess
import re
import shutil

licence={"003e0ba0126b":"kamera_dilna"} # MAC adresy kamer jako slovnik

zdroj = "/var/autoshare_sata/NSA310_2TB/kamery/"
cil = "/var/autoshare_sata/NSA310_2TB/video/"

testOnly = False
verbse = True

zbyvajiciMistoNaDisku = 10 # kdyz je na disku alespon 10% volneho mista
hledanePriponySouboru = (".h264",)
casovyTest = 60*2 # 2 minuty
myhost = os.uname()[1]

class VideoFileTranscoder(object):
    """
    kamery vytvari na ftp serveru podadresare, jejichz nazev se sklada 
    z IP adresy kamery, podtrzitka a MAC adresy kamery, napriklad:
    172.18.0.136_003e0ba0126b    
    Adresare, ktere vytvori kamery pri ftp prenosu maji dalsi 
    podadresarovou strukturu v nasledujicim tvaru:
    
    |-172.18.0.136_003e0ba0126b
    |---2013-12-30
    |-----01
    |-------rec
    |----------16.17.37-16.17.38[M][@121][0].h264
    
    tzn datum(y), cislo streamu dane kamery a v adresari rec jednotlive video soubory.
    
    Nazev souboru ma tvar: 16.17.37-16.17.38[M][@121][0].h264
    Tzn. cas zacatku video zaznamu, cas konce video zaznamu, dale nam neznama cisla a extenzi (.h264).
    
    Skript rekurzivne prochazi zdrojovy adresar, v nazvech podadresaru hleda substring, 
    ktery odpovida MAC adresam kamer, ktere jsou "povoleny" (licencovany).  
    Nalezene soubory skript prevede pomoci command line prikazu pomoci utility ffmpeg
    (ktera musi byt nainstalovana) do formatu avi a umisti je do ciloveho adresare.
    Po uspesne konverzi (po vytvoreni ciloveho souboru avi) je zdrojovy soubor vymazan.
    V pripade, ze je videosoubor poruseny a utilita ffmpeg vrati chybovy navratovy kod,
    je naruseny soubor premisten do ciloveho adresare do podadresare s nazvem corrupted.
        
#TODO: odesilani mailu neni doreseno
    V pripade, ze je v cilovem adresari malo mista, odesle na zadanou adresu varovny mail
    """
    def __init__(self, sourceDir, targetDir, verbose, testOnly):
        self.sourceDir = sourceDir
        self.targetDir = targetDir        
        self.verbose = verbose
        self.testOnly = testOnly # neprovadi konverzi, ani vymazani souboru
        self.freeDiskSpacePercent = self.freeDiskSpace()        
       
    def freeDiskSpace(self):        
        """Return disk usage statistics about the given path.    
        Returned value is a tuple with attributes 'total', 'used' and 'free',
        which are the amount of total, used and free space, in bytes.        
        """
        st = os.statvfs(self.targetDir)
        free = st.f_bavail * st.f_frsize
        total = st.f_blocks * st.f_frsize
        used = (st.f_blocks - st.f_bfree) * st.f_frsize        
        freeDiskSpacePercent = (free*1.0/total)*100  
        if self.verbose:
            print "%0.2f%% kapacity disku je volnych" % freeDiskSpacePercent
        return freeDiskSpacePercent
    
    def findFiles(self):        
#TODO: overit, ze soubor neni otevren (neslo by to obejit tak, ze konvertujeme pouze soubory sarsi nez napriklad 5 min?)
        for root, dirs, files in os.walk(self.sourceDir):            
            for file in files:                
                if file.endswith(hledanePriponySouboru):
                    self.fullFileName = os.path.join(root, file)
                    #print self.fullFileName                    
                    if os.path.isfile(self.fullFileName):
                        for key in licence.keys():
                            if key in self.fullFileName: # kdyz je MAC adresa kamery v definici licenci
                                self.cameraDirName = licence[key]
#TODO: co delat, kdyz nebude znam recordingDate, nebo recordintTimeInterval                                
                                self.recordingDate = re.findall('20\d{2}[-][0-1][0-2][-][0-3][0-9]', self.fullFileName)[0]
                                self.recordingTimeInterval = re.findall('[0-2][0-9][.][0-5][0-9][.][0-5][0-9][-][0-2][0-9][.][0-5][0-9][.][0-5][0-9]', self.fullFileName)[0]                                
                                info = os.stat(self.fullFileName)
                                self.fileTimeModification = info[ST_MTIME]
                                self.fileName = file                                
                                yield self.fullFileName
                                

    def makeSurePahtExists(self, dirForCorruptedFile = ""):        
        self.targetPath = self.targetDir + "/" + self.cameraDirName + "/" + dirForCorruptedFile + "/"
        self.targetPath = self.targetPath.replace('//','/')
        if ".recycle/" in self.targetPath or ".Trash" in self.targetPath:
            if self.verbose:
                print "recycle bin nezalohujeme"
            return 
        if self.testOnly:
#            print "testovací režim, adresář: %s nebude vytvořen" % self.targetPath
            return
        try:
            # vytvorime novy adresar a nastavime stejna prava jako u adresare /video 
            # tzn: RWE by owner, RWE by group, RWE by others      
            os.makedirs(self.targetPath, S_IRWXU | S_IRWXG | S_IRWXO) 
            os.chmod(self.targetPath, S_IRWXU | S_IRWXG | S_IRWXO) 
            if self.verbose:
                print "vytvoril jsem adresar: %s" % self.targetPath
        except OSError:
            if os.path.isdir(self.targetPath):
                # We are nearly safe
                pass                
            else:
                # There was an error on creation, so make sure we know about it
                raise
          
        
    def moveCorruptedFile(self):
        self.makeSurePahtExists(dirForCorruptedFile="corrupted")
        self.corruptedFileName = "%s%s_%s.h264" % (self.targetPath, self.recordingDate, self.recordingTimeInterval)
        shutil.move(self.fullFileName, self.corruptedFileName)
        if self.verbose:
            print "*"*10
            print "nazlezen poskozeny soubor %s" % self.fullFileName   
            print "*"*10
                
    def createTranscodedFile(self):        
        self.errorFound = False
        nazevSouboru = "%s%s_%s" % (self.targetPath, self.recordingDate, self.recordingTimeInterval)         
        subprocessList = ['/ffp/bin/ffmpeg', '-f', 'h264', '-i', '%s' % self.fullFileName, '-vcodec', 'copy', '-acodec', 'copy', '%s.avi' % nazevSouboru, '-y']
        subprocessString = " ".join(subprocessList)
        myCatch = subprocess.Popen(subprocessString, stdout = subprocess.PIPE, stderr=subprocess.PIPE, shell = True)
        output, err = myCatch.communicate()
        exitCode = myCatch.wait()
        if exitCode:
            self.errorFound = True
        if self.verbose:
            print "*"*10
            print "output: ", type(output), output
            print "*"*10
            print "err: ", type(err), err
            print "*"*10            
            print "exitCode: ",  type(exitCode), exitCode
            print "*"*10
    
    def deleteSourceFile(self):        
        os.remove(self.fullFileName)
        
                
    def timeTestOk(self):
        now = time.time()
        if (now - self.fileTimeModification) > casovyTest:
            return True
            
    def scripAllreadyRunning(self):
        myCatch = subprocess.Popen(["/ffp/bin/ps aux | grep zyxel_main.py"], stdout = subprocess.PIPE, shell = True)
        output, err = myCatch.communicate()        
        count = output.count("zyxel_main.py")
        """
        oputput by mel vypadat nejak takto, proto bychom meli napocitat 4 vyskyty zyxel_main.py:
        root     27514  0.0  0.2   2724   512 ?        SN   15:33   0:00 /bin/sh -c /usr/bin/python /usr/local/zy-pkgs/skripty/zyxel_main.py > /usr/local/zy-pkgs/skripty/backup.log 2>&1
        root     27515  0.0  1.4   5820  3576 ?        SN   15:33   0:00 /usr/bin/python /usr/local/zy-pkgs/skripty/zyxel_main.py
        root     27523  0.0  0.1   2724   476 ?        SN   15:33   0:00 /bin/sh -c /ffp/bin/ps aux | grep zyxel_main.py
        root     27525  0.0  0.2   2728   552 ?        SN   15:33   0:00 grep zyxel_main.py
        """
        
        if self.verbose:
            print output
            print "count:", count
        if count > 4:
            return True

if __name__ == "__main__":    
    if not os.path.exists(zdroj):
        print "cesta %s neexistuje" % zdroj
        sys.exit()
    if not os.path.exists(cil):
        print "cesta %s neexistuje"% cil
        sys.exit()


    tCoder = VideoFileTranscoder(zdroj, cil, verbse, testOnly)    
    if tCoder.scripAllreadyRunning():
        print "predchozi spusteny skript jeste bezi"
        sys.exit()    
    if tCoder.freeDiskSpacePercent >= zbyvajiciMistoNaDisku:
        fileGen = tCoder.findFiles()
        while 1:            
            try:
                next(fileGen)                
                try:
                    tCoder.makeSurePahtExists()
                    if tCoder.timeTestOk():
                        tCoder.createTranscodedFile()
                        if not tCoder.errorFound:
                            tCoder.deleteSourceFile()
                        else:
                            tCoder.moveCorruptedFile()                         
                except Exception, e:
                    print Exception, e
                   
        
            except StopIteration:
                if tCoder.verbose:                    
                    print "zalohovani uspesne dokonceno"
                break
    else:
        pass
#TODO: na NSA310 je na mailovani msmtp (/usr/sbin/msmtp)
# podrobnosti jak na to: 
# http://devblog.virtage.com/2013/05/email-sending-from-ubuntu-server-via-google-apps-smtp-with-msmtp/

# toto nefunguje
#        import mailer
#        address = ["petr.jakes.tpc@gmail.com", ]
#        text = "%s - na disku není dostatek místa, soubory z kamer nebyly transkodovány" % myhost
#        text = text.decode('utf-8')
#        text = text.encode('CP1250')
#        subject = "%s - nedostatek místa na disku" % myhost
#        subject = subject.decode('utf-8')
#        subject = subject.encode('CP1250')
#        mailer.newSendMail(text= text, subject = subject, to = address)

